<?php

  /**
   * WebApplication
   *
   * Класс приложения
   *
   * @property-read bool   $isAjaxRequest признак того, что данные запрошены через XMLHTTPRequest
   * @property-read User   $authUser      экземпляр модели авторизованного пользователя
   * @property-read string $contentPath   путь к папке с внешними ресурсами
   * @property-read string $contentUrl    URL папки с внешними ресурсами
   */

  final class WebApplication extends CWebApplication
  {
  // константы для работы с Open API
    const VK_APP_ID            = 2270150;
    const VK_APP_SHARED_SECRET = "SIjlXvPO3MvzMKhSsslX";

    /**
     * внешние модули
     *
     * @var string[]
     */
    public $vendors = array();

    /**
     * основное меню сайта
     *
     * @var array
     */
    public $menu = array();

    /**
     * заголовки страниц
     *
     * @var string[]
     */
    public $titles = array();

    /**
     * кол-во постов на стартовой странице
     *
     * @var int
     */
    public $postLimit = 3;

    /**
     * конструктор
     *
     * @param mixed $config конфигурация приложения (массив или имя файла)
     * @access public
     */
    public function __construct($config = null)
    {
      parent::__construct($config);

      mb_internal_encoding("utf-8");

      /** @var $aMan CAssetManager */
      $aMan = $this->getAssetManager();
      $aMan->setBaseUrl($this->request->getBaseUrl().$aMan->getBaseUrl());
      $this->setLayoutPath($this->getViewPath());
      Record::$db = $this->getDb();

    // регистрация внешних модулей
      spl_autoload_unregister(array("YiiBase", "autoload"));
      array_walk($this->vendors, create_function('$v', 'Yii::import("application.vendors.$v", true);'));
      spl_autoload_register(array("YiiBase", "autoload"));
    }

    /**
     * настройка приложения
     *
     * @param array $config настройки
     * @return void
     * @access public
     */
    public function configure($config)
    {
      if (defined("YII_DEBUG") && YII_DEBUG && is_array($config))
        parent::configure(self::configMerge(array(
          "preload"    => array("log"),
          "components" => array(
            "log" => array(
              "class"  => "CLogRouter",
              "routes" => array(array(
                  "class"      => "CFileLogRoute",
                  "levels"     => "trace,info",
                  "categories" => "system.*,application.*"
              ))
            ),
            "db" => array(
              "enableProfiling"    => true,
              "enableParamLogging" => true
            ),
          ),
          "vendors" => array("FirePHP.FB")
        ), $config));
      else
        parent::configure($config);
    }

    /**
     * преобразовать дату из формата MySQL в формат, используемый приложением
     *
     * @param string $mysqlDate дата в формате MySQL
     * @param bool   $onlyDate  только дата
     * @return string
     * @access public
     */
    public function frmDate($mysqlDate = "", $onlyDate = true)
    {
      if ($mysqlDate)
      {
        list($year, $month, $day, $h, $m, $s) = sscanf($mysqlDate, "%d-%d-%d %d:%d:%d");
        $date = mktime($h, $m, $s, $month, $day, $year);
      }
      else
        $date = mktime();

      return CHtml::encode($this->getDateFormatter()->formatDateTime($date, "long", $onlyDate ? null : "short"));
    }

    /**
     * вернуть присланный параметр
     *
     * @param string $name имя параметра
     * @return mixed
     * @access public
     */
    public function getParam($name)
    {
      return $this->getRequest()->getParam($name);
    }

    /**
     * данные запрошены через XMLHTTPRequest
     *
     * @return bool
     * @access public
     */
    public function getIsAjaxRequest()
    {
      return $this->request->getIsAjaxRequest() || $this->getParam("ajax");
    }

    /**
     * экземпляр модели авторизованного пользователя
     *
     * @return User
     * @access public
     */
    public function getAuthUser()
    {
      /** @var $user CWebUSer */
      $user = $this->getUser();
      return $user->getIsGuest() ? null : User::model()->findByPk($user->getState("id"));
    }

    /**
     * путь к папке с внешними ресурсами
     *
     * @return string
     * @access public
     */
    public function getContentPath()
    {
      return $this->getAssetManager()->getBasePath();
    }

    /**
     * URL папки с внешними ресурсами
     *
     * @return string
     * @access public
     */
    public function getContentUrl()
    {
      return $this->getAssetManager()->getBaseUrl();
    }

    /**
     * проверка авторизации ВКонтакте
     *
     * @return bool
     * @access public
     */
    public static function authOpenAPI()
    {
      static $validKeys = array("expire", "mid", "secret", "sid", "sig");

      if ($appCookie = $_COOKIE["vk_app_".self::VK_APP_ID])
      {
        $session = array();
        foreach (explode("&", $appCookie) as $param)
        {
          list($key, $value) = explode("=", $param, 2);
          ($key && $value && in_array($key, $validKeys)) and ($session[$key] = $value);
        }
        ksort($session);
        $sign = "";
        foreach ($session as $key => $value)
          strcasecmp($key, "sig") and ($sign .= "$key=$value");
        return ($session["sig"] == md5($sign.self::VK_APP_SHARED_SECRET)) && (time() < $session["expire"]);
      }

      return false;
    }

    /**
     * собрать путь, в сообтветствии с системным разделителем
     *
     * @param string[] $pieces названия папок и файлов
     * @return string
     * @access public
     */
    public static function createPath(array $pieces)
    {
      return join(DIRECTORY_SEPARATOR, $pieces);
    }

    /**
     * проверить валидность выражения
     * в случае невалидности генерируется исключение
     *
     * @param bool   $expression выражение
     * @param int    $status     номер генерируемого исключения
     * @param string $message    сообщение об ошибке
     * @return void
     * @access public
     */
    public static function validate($expression, $status = 400, $message = null)
    {
      if (!$expression)
        throw new CHttpException($status, $message);
    }

    /**
     * ответ на ajax-запрос
     *
     * @param mixed  $response    возвращаемые данные
     * @param string $contentType Content-type
     * @return void
     * @access public
     */
    public static function response($response, $contentType = "")
    {
      $contentType or ($contentType = is_array($response) ? "application/json" : "text/plain");
      header("Content-Type: $contentType;charset=utf-8");
      echo is_array($response) ? json_encode($response) : $response;
    }

    /**
     * объединить конфигурационные настройки
     *
     * @param array $a1  умолчательная конфигурация
     * @param array &$a2 заданная конфигурация
     * @return array
     * @access private
     */
    private static function configMerge(array $a1, array &$a2)
    {
      foreach ($a2 as $key => &$value)
      {
        (array_key_exists($key, $a1) && is_array($a1[$key]) && is_array($value)) and ($value = self::configMerge($a1[$key], $value));
        is_string($key) ? $a1[$key] = $value : $a1[] = $value;
      }
      return $a1;
    }
  }

?>